package merisis.traitspace.impl;

import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;

import merisis.Mark;
import merisis.MerisisVM;
import merisis.sense.SenseMaker;
import merisis.traitspace.History;
import merisis.traitspace.PointGroup;
import merisis.util.BitUtil;

import org.apache.solr.util.OpenBitSet;

import com.google.inject.Inject;

public class TraitSpaceImpl implements MerisisVM {

	@Inject
	private History history;

	@Inject
	private SenseMaker sensemaker;

	@Inject
	private Logger logger;

	private ThreadLocal<ProcessContext> processces = new ThreadLocal<ProcessContext>();

	public void stepin() {

		ProcessContext ctx = getProcessContext();

		Stack<HistoryView> viewStack = ctx.viewStack;
		OpenBitSet hvbound = new OpenBitSet();
		hvbound.or(ctx.objective);
		if (!viewStack.isEmpty()) {
			hvbound.or(viewStack.peek().background);
		}

		HistoryView hv = new HistoryView();
		hv.background = hvbound;

		viewStack.push(hv);

		SketchImpl sketch = ctx.sketch;
		sketch.setBackground(hv.background);
		sketch.setSource(null);

		ctx.objective = null;
	}

	public void source() {

		ProcessContext ctx = getProcessContext();

		HistoryView hv = ctx.viewStack.peek();

		SketchImpl sketch = ctx.sketch;
		sketch.setBackground(hv.background);
		sketch.setSource(ctx.objective);
		ctx.objective = null;
	}

	public void autobeam() {

		ProcessContext ctx = getProcessContext();

		Stack<HistoryView> viewStack = ctx.viewStack;

		if (!viewStack.isEmpty()) {
			OpenBitSet hvbound = null;
			for (int i = 0; i < viewStack.size()
					&& (hvbound == null || hvbound.isEmpty()); i++)
				hvbound = viewStack.get(i).background;

			HistoryView hv = new HistoryView();
			hv.background = hvbound;
			viewStack.push(hv);

			SketchImpl sketch = ctx.sketch;
			sketch.setBackground(hv.background);
			sketch.setSource(null);

			if (logger.isLoggable(Level.FINEST)) {
				StringBuilder sb = new StringBuilder();
				sb.append("beamed view: {");
				BitUtil.format(sb, hvbound, ",");
				sb.append("}");
				logger.finest(sb.toString());
			}
		}
	}

	public void stepout() {

		ProcessContext ctx = getProcessContext();
		ctx.viewStack.pop();

		SketchImpl sketch = ctx.sketch;
		sketch.setBackground(ctx.viewStack.peek().background);
		sketch.setSource(null);
	}

	public void focus() {

		ProcessContext ctx = getProcessContext();

		SketchImpl sketch = ctx.sketch;
		sketch.setPart(ctx.objective);
		ctx.objective = sketch.focus();
	}

	public void tprism() {

		ProcessContext ctx = getProcessContext();

		ctx.objective = ctx.sketch.tspectrum(ctx.objective);
	}

	public void complement() {

		ProcessContext ctx = getProcessContext();

		ctx.objective = ctx.sketch.complement(ctx.objective);
	}
	
	
	public void adopt() {
		// TODO Auto-generated method stub
		
	}

	public void setfocus() {

		ProcessContext ctx = getProcessContext();

		ctx.sketch.setFocus(ctx.objective);
	}

	public void roll(long roll) {

		ProcessContext ctx = getProcessContext();

		ctx.objective = ctx.sketch.historical().rollHistory((int) roll);
	}

	public void give(long... dots) {

		ProcessContext ctx = getProcessContext();
		ctx.objective = new OpenBitSet();

		BitUtil.bitsetOrArray(ctx.objective, dots);
	}

	public Mark mark() {

		throw new UnsupportedOperationException();
	}

	public long[] read() {

		ProcessContext ctx = getProcessContext();
		OpenBitSet r = ctx.objective;
		if (r == null || r.isEmpty())
			return null;

		return BitUtil.toArray(r);
	}

	public long[] read(long bookmark) {

		throw new UnsupportedOperationException();
	}

	public PointGroup readPointGroup() {

		ProcessContext ctx = getProcessContext();
		OpenBitSet r = ctx.objective;

		return new BitSetPointIterator(r);
	}

	public long[] background() {

		ProcessContext ctx = getProcessContext();

		OpenBitSet onto = ctx.viewStack.peek().background;

		return BitUtil.toArray(onto);
	}

	private ProcessContext getProcessContext() {

		ProcessContext ctx = processces.get();
		if (ctx == null) {
			ctx = new ProcessContext(history);
			processces.set(ctx);

			HistoryView hv = new HistoryView();
			hv.background = new OpenBitSet();

			ctx.viewStack.push(hv);
		}

		return ctx;
	}

	public long occur(long... ontos) {

		ProcessContext ctx = getProcessContext();

		long occurence = sensemaker.sense();

		if (ontos != null) {
			for (int i = 0; i < ontos.length; i++) {
				history.emerge(ontos[i], occurence);
			}
		}

		OpenBitSet backgrounds = ctx.viewStack.peek().background;
		for (long bg = backgrounds.nextSetBit(0); bg >= 0; bg = backgrounds
				.nextSetBit(bg + 1)) {

			history.emerge(bg, occurence);
		}

		return occurence;
	}

	public class BitSetPointIterator implements PointGroup {

		private OpenBitSet s;

		public BitSetPointIterator(OpenBitSet s) {
			this.s = s;
		}

		public long next(long point) {

			if (s == null)
				return -1;

			return s.nextSetBit(point + 1);
		}
	}
}